import numpy as np


class PCA:


    def __init__(self, n_components=None):
        """
        初始化参数

        参数:
        n_components (int 或 None): 要保留的主成分数量，如果为None，则保留所有主成分
        """
        self.n_components = n_components
        self.components_ = None
        self.explained_variance_ = None
        self.explained_variance_ratio_ = None

    def fit(self, X):
        """
        对输入数据进行主成分分析，拟合数据以获取主成分等相关信息

        参数:
        X (numpy.ndarray): 输入的数据，形状为 (n_samples, n_features)，即样本数乘以特征数

        返回:
        self: 返回自身对象，以便可以链式调用（如 pca.fit(X).transform(X_new) 等）
        """
        # 数据中心化（减去均值）
        self.mean_ = np.mean(X, axis=0)
        X_centered = X - self.mean_

        # 计算协方差矩阵
        cov_matrix = np.cov(X_centered.T)

        # 对协方差矩阵进行特征值分解
        eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)

        # 将特征值和特征向量按特征值从大到小排序
        sorted_indices = np.argsort(eigenvalues)[::-1]
        sorted_eigenvalues = eigenvalues[sorted_indices]
        sorted_eigenvectors = eigenvectors[:, sorted_indices]

        if self.n_components is None:
            self.n_components = X.shape[1]

        # 选择前n_components个主成分对应的特征向量
        self.components_ = sorted_eigenvectors[:, :self.n_components]

        # 计算解释方差和解释方差比例
        self.explained_variance_ = sorted_eigenvalues[:self.n_components]
        total_variance = np.sum(sorted_eigenvalues)
        self.explained_variance_ratio_ = self.explained_variance_ / total_variance

        return self

    def transform(self, X):
        """
        使用拟合好的主成分对输入数据进行降维

        参数:
        X (numpy.ndarray): 输入的数据，形状为 (n_samples, n_features)

        返回:
        X_transformed (numpy.ndarray): 降维后的数据，形状为 (n_samples, n_components)
        """
        X_centered = X - self.mean_
        return np.dot(X_centered, self.components_)